﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Somnus.Core.SceneManager;
using FarseerPhysics;
using FarseerPhysics.DebugViews;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics.Collision;
using Somnus.Utils;
using Somnus.Objects;
using FarseerPhysics.Common;

namespace Somnus.Core
{
    class DreamWorld : World 
    {
        private float _scaleFactor;
        private List<PhysicsObject> _objectList;
        private List<Layer2> _layerList;
        private DebugViewXNA _debugView;
        private static DreamWorld _instance;

        public Camera2d camera;
        public Boolean enableDebugView;
        public Viewport viewport;
        

        public DreamWorld(Vector2 gravity, float scaleFactor)
            : base(gravity)
        {
            _instance = this;

            _scaleFactor = scaleFactor;

            _objectList = new List<PhysicsObject>();
            camera = new Camera2d(viewport);
            camera.Limits = new Rectangle(0, 0, 8000, 600);
            _debugView = new DebugViewXNA(this);
            _debugView.AppendFlags(DebugViewFlags.DebugPanel);
            _debugView.DefaultShapeColor = Color.Purple;
            _debugView.SleepingShapeColor = Color.Pink;
            _debugView.LoadContent(Game1.getInstance().GraphicsDevice, Game1.getInstance().Content);


            _layerList = new List<Layer2>
            {
                new Layer2(camera) { Parallax = new Vector2(0.0f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(0.1f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(0.2f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(0.3f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(0.4f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(0.5f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(0.6f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(0.8f, 1.0f) },
                new Layer2(camera) { Parallax = new Vector2(1.0f, 1.0f) }
            };

            Settings.MaxPolygonVertices = 8;
            enableDebugView = false;
        }



        public void addBG(int k, string textura)
        {
            _layerList[k].Bgs.Add(new Background(this, Game1.getInstance().Content.Load<Texture2D>(textura)));
        }

        public void addObject(PhysicsObject physicsObject)
        {
            physicsObject.Name = verifyValidName(physicsObject.Name);


            _objectList.Add(physicsObject);
        }

        private string verifyValidName(string objectName)
        {
            Boolean isNomeOk = true;
            int failureCount = 0;

            do
            {
                isNomeOk = true;
                foreach (PhysicsObject po in _objectList)
                {
                    if (objectName == po.Name)
                    {
                        isNomeOk = false;
                        if (failureCount == 0)
                        {
                            failureCount++;
                            objectName = objectName + failureCount.ToString();
                            break;
                        }
                        else
                        {
                            failureCount++;
                            objectName = objectName.Replace((failureCount - 1).ToString(), failureCount.ToString());
                            break;
                        }
                    }
                }
            }
            while (!isNomeOk);

            return objectName;
        }

        public void update(GameTime gameTime)
        {
            updateObjects(gameTime);
            verifyDebugControlls();
        }

        private void verifyDebugControlls()
        {
            if (Controller.KeyPressed(Keys.F1))
            {
                enableDebugView = !enableDebugView;
            }
        }

        private void updateObjects(GameTime gameTime)
        {
            List<PhysicsObject> garbage = new List<PhysicsObject>();
            
            //Verifica se o objeto está marcado para morrer
            //se está adiciona na lista de garbage
            //Se não, passa o comando de Update para o objeto.
            for (int i = 0; i < _objectList.Count; i++)
            {
                if (_objectList[i].kill)
                {
                    garbage.Add(_objectList[i]);
                }
                else
                {
                    _objectList[i].update(gameTime);
                }
            }

            //Mata a garbage
            for (int i = 0; i < garbage.Count; i++)
            {
                _objectList.Remove(garbage[i]);
                garbage[i].destroy();
            }

        }



        public void draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            // Draw de layers + parallax
            foreach (Layer2 layer in _layerList)
            {
                 layer.Draw(gameTime, spriteBatch);
            }
            
            spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, null, null, null, null, camera.get_transformation(Game1.getInstance().graphics));

            for (int i = 0; i < _objectList.Count; i++)
            {
                _objectList[i].draw(gameTime, spriteBatch);
            }

            spriteBatch.End();

            if (enableDebugView)
            {
                Matrix projection = Matrix.CreateOrthographicOffCenter(0f, Game1.getInstance().GraphicsDevice.Viewport.Width / ScaleFactor,
                                                                 Game1.getInstance().GraphicsDevice.Viewport.Height / ScaleFactor, 0f, 0f,
                                                                 1f);
                Vector2 _screenCenter = new Vector2(Game1.getInstance().GraphicsDevice.Viewport.Width / 2f,
                                                    Game1.getInstance().GraphicsDevice.Viewport.Height / 2f);

                Matrix view = Matrix.CreateTranslation(new Vector3(-camera.Pos.X / (ScaleFactor), -camera.Pos.Y / ScaleFactor, 0f))
                                * Matrix.CreateTranslation(new Vector3((_screenCenter / ScaleFactor), 0f))
                                * Matrix.CreateTranslation(new Vector3(camera.offsetX / ScaleFactor, camera.offsetY / ScaleFactor, 0f));

                _debugView.RenderDebugData(ref projection, ref view);
            }
        }


        #region GETS & SETS

        static public DreamWorld getWorld()
        {
            return _instance;
        }

        public float ScaleFactor
        {
            get
            {
                return _scaleFactor;
            }
        }

        //Funciona
        public PhysicsObject getObjectByName(String name)
        {
            for (int i = 0; i < _objectList.Count; i++)
            {
                if (_objectList[i].Name == name)
                    return _objectList[i];
            }
            return null;
        }

        public PhysicsObject getObjectByType(Type type)
        {

            for (int i = 0; i < _objectList.Count; i++)
            {
                if (_objectList[i].GetType().IsSubclassOf(type) || _objectList[i].GetType() == type)
                    return _objectList[i];
            }
            return null;
        }

        public List<PhysicsObject> getObjectsByType(Type type)
        {
            List<PhysicsObject> listByType = new List<PhysicsObject>();

            for (int i = 0; i < _objectList.Count; i++)
            {
                if (_objectList[i].GetType().IsSubclassOf(type) || _objectList[i].GetType() == type)
                    listByType.Add(_objectList[i]);
            }
            return listByType;
        }

        #endregion

        public void CreateMap(string levelName)
        {
            List<Dictionary<String, Object>> objectList = GleedToArray.getLevelList(levelName);

            foreach (Dictionary<String, Object> objeto in objectList)
            {
                PhysicsObject novoObjeto = new PhysicsObject(this, (String)objeto["name"], (Texture2D)objeto["texture"], null, BodyType.Static, (float)objeto["x"], (float)objeto["y"], (float)objeto["width"], (float)objeto["height"]);
                novoObjeto.Rotation = (float)objeto["rotation"];
            }
        }
    }
}
